package com.via.custom.generator;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;

/**
 * Mybatis-Plus 代码自动生成器
 *
 * @author Gumeng
 */
public class MyBatisPlusGenerator {

    /**
     * 包的根位置
     */
    private static final String PACKAGE_PREFIX = "com.via.custom.impl";

    /**
     * 数据库表的公共字段
     */
    private static final String[] STRATEGY_SUPER_ENTITY_COLUMNS = {"is_deleted", "create_time", "update_time"};

    /**
     * 代码作者
     */
    private static final String AUTHOR = "Gumeng";

    /**
     * 项目地址
     */
    private static String projectPath;

    /**
     * 生成器启动类
     * 启动后输入表名可生成对应 Controller Service Mapper Entity
     *
     * @param args args
     */
    public static void main(String[] args) {
        // 目标数据表名
        List<String> tableNameList = getScannerTableNameList();
        generateByTables(tableNameList);
    }

    /**
     * 获取数据库列表
     *
     * @return 数据库列表列表
     */
    public static List<String> getScannerTableNameList() {
        System.out.println("输入数据库表名，用#号结束：");
        List<String> tableNameList = new ArrayList<>();
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNext()) {
            String input = scanner.next();
            if (!StringUtils.hasText(input)) {
                continue;
            }
            if ("#".equals(input)) {
                return tableNameList;
            }
            tableNameList.add(input);
        }

        return null;
    }

    /**
     * 生成过程
     *
     * @param tableNameList 数据库表名列表
     */
    private static void generateByTables(List<String> tableNameList) {
        AutoGenerator autoGenerator = new AutoGenerator();
        autoGenerator.setTemplateEngine(new VelocityTemplateEngine());

        // 全局配置
        GlobalConfig globalConfig = globalConfigGenerate();
        // 包配置
        PackageConfig packageConfig = packageConfigGenerate();
        // 文件路径输出配置
        List<FileOutConfig> fileOutConfigs = fileOutConfigGenerate();
        // 其他参数
        InjectionConfig injectionConfig = injectionGenerate(fileOutConfigs);
        // 数据库配置
        DataSourceConfig dataSourceConfig = dataSourceConfigGenerate();
        // 自定义模板
        TemplateConfig templateConfig = templateConfigGenerate();
        // 表字段映射策略配置
        StrategyConfig strategy = strategyConfigGenerate(tableNameList);

        autoGenerator.setGlobalConfig(globalConfig);
        autoGenerator.setDataSource(dataSourceConfig);
        autoGenerator.setPackageInfo(packageConfig);
        autoGenerator.setCfg(injectionConfig);
        autoGenerator.setTemplate(templateConfig);
        autoGenerator.setStrategy(strategy);
        autoGenerator.execute();
    }

    /**
     * 数据库源配置
     *
     * @return DataSourceConfig
     */
    private static DataSourceConfig dataSourceConfigGenerate() {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/mp-generator.properties"));

        // 读取 properties 配置
        Properties properties;
        try {
            propertiesFactoryBean.afterPropertiesSet();
            properties = Objects.requireNonNull(propertiesFactoryBean.getObject());
        } catch (IOException e) {
            throw new RuntimeException("application.properties 配置读取失败");
        }

        String url = properties.getProperty("generator.datasource.url");
        String driver = properties.getProperty("generator.datasource.driver-class-name");
        String username = properties.getProperty("generator.datasource.username");
        String password = properties.getProperty("generator.datasource.password");

        // 数据库配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setDbType(DbType.MYSQL);
        dataSourceConfig.setUrl(url);
        dataSourceConfig.setDriverName(driver);
        dataSourceConfig.setUsername(username);
        dataSourceConfig.setPassword(password);

        return dataSourceConfig;
    }

    /**
     * 全局配置
     *
     * @return GlobalConfig
     */
    private static GlobalConfig globalConfigGenerate() {
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setAuthor(AUTHOR);
        globalConfig.setFileOverride(true);

        // 生成后是否自动打开文件
        globalConfig.setOpen(false);

        // 获取子项目目录
        String propertyPath = System.getProperty("user.dir");
        String path = Objects.requireNonNull(MyBatisPlusGenerator.class.getClassLoader().getResource("")).getPath();
        String levelPath = path.substring(0, path.indexOf("target") - 1);
        if (!propertyPath.equals(levelPath)) {
            projectPath = levelPath;
        } else {
            projectPath = propertyPath;
        }

        // 输出路径
        globalConfig.setOutputDir(projectPath + "/src/main/java");

        // 自定义文件命名，%s会自动填充表实体属性
        globalConfig.setControllerName("%sController");
        globalConfig.setServiceName("I%sService");
        globalConfig.setServiceImplName("%sServiceImpl");
        globalConfig.setEntityName("%s");
        globalConfig.setMapperName("%sMapper");

        // XML配置
        globalConfig.setXmlName("%sMapper");
        // mapper.xml中生成基础 ResultMap
        globalConfig.setBaseResultMap(true);
        // mapper.xml中生成基础 ColumnList
        globalConfig.setBaseColumnList(true);

        // 二级缓存
        globalConfig.setEnableCache(false);
        // 主键类型 雪花ID
        globalConfig.setIdType(IdType.ASSIGN_ID);

        return globalConfig;
    }

    /**
     * 表字段映射策略配置
     *
     * @param tableNameList 数据库表名列表
     * @return StrategyConfig
     */
    private static StrategyConfig strategyConfigGenerate(List<String> tableNameList) {
        StrategyConfig strategy = new StrategyConfig();

        // 设置表名
        String[] tables = tableNameList.toArray(new String[0]);
        strategy.setInclude(tables);
        // 表名映射到实体类的策略，下划线转驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        // 列名映射到属性的策略，下划线转驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 表名映射到实体名称去掉前缀
//        strategy.setTablePrefix("");
        // 表名和字段名是否大小写
        strategy.setCapitalMode(false);
        // 布尔类型字段是否移除is前缀
        strategy.setEntityBooleanColumnRemoveIsPrefix(false);
        // 实体类使用Lombok
        strategy.setEntityLombokModel(true);
        // 实体类链式模式
        strategy.setChainModel(true);

        // Controller使用rest接口模式
        strategy.setRestControllerStyle(true);
        // 驼峰转连字符-
        strategy.setControllerMappingHyphenStyle(true);

        // 写于父类中的公共字段
        strategy.setSuperEntityColumns(STRATEGY_SUPER_ENTITY_COLUMNS);

        // 继承顶层
        strategy.setSuperControllerClass(PACKAGE_PREFIX + ".base.BaseController");
        strategy.setSuperEntityClass("com.via.core.db.base.BaseEntity");
        strategy.setSuperServiceClass("com.via.core.db.base.BaseService");
        strategy.setSuperServiceImplClass("com.via.core.db.base.BaseServiceImpl");
        return strategy;
    }

    /**
     * 自定义模板
     *
     * @return TemplateConfig
     */
    private static TemplateConfig templateConfigGenerate() {
        TemplateConfig templateConfig = new TemplateConfig();

//        templateConfig.setController("templates/controller.java.vm")
//                .setEntity("templates/entity.java.vm")
//                .setService("templates/service.java.vm")
//                .setServiceImpl("templates/serviceImpl.java.vm")
//                .setMapper("templates/mapper.java.vm")

        // 排除源码包内的Mapper
        templateConfig.setXml(null);
        return templateConfig;
    }

    /**
     * 文件路径输出配置
     *
     * @return 文件路径输出配置
     */
    private static List<FileOutConfig> fileOutConfigGenerate() {
        String templatePath = "/templates/mapper.xml.vm";

        // 自定义输出配置
        List<FileOutConfig> fileOutConfigs = new ArrayList<>();
        // 自定义配置会被优先输出
        fileOutConfigs.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名，如果Entity设置了前后缀，此处xml的名称也会跟着变化
                return projectPath + "/src/main/resources/mapper/"
                        + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        return fileOutConfigs;
    }

    /**
     * 其他参数，在velocity使用的参数，格式：${cfg.version}
     *
     * @param fileOutConfigList 文件路径输出配置
     * @return InjectionConfig
     */
    private static InjectionConfig injectionGenerate(List<FileOutConfig> fileOutConfigList) {
        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>(1);
                map.put("version", "1.0");
                this.setMap(map);
            }
        };
        injectionConfig.setFileOutConfigList(fileOutConfigList);
        return injectionConfig;
    }

    /**
     * 包配置
     *
     * @return 包配置
     */
    private static PackageConfig packageConfigGenerate() {
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setModuleName("");
        packageConfig.setParent(PACKAGE_PREFIX);
        packageConfig.setEntity("entity");
        packageConfig.setService("service");
        packageConfig.setServiceImpl("service.impl");
        packageConfig.setMapper("mapper");

        // 不要设置包目录，否则会生成到源码包内
        packageConfig.setXml(null);
        return packageConfig;
    }
}
